#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "_image.h"
#include "image.h"
#include "_png.h"
#include "_jpeg.h"
#include "png.h"
#include "_osd.h"


#define false               0
#define true                1

#define __DEBUG__

#ifdef __DEBUG__
#define __E(fmt, args...) fprintf(stderr, "IMAGE ERROR [%d]: " fmt, __LINE__, ## args)
#define __W(fmt, args...) fprintf(stderr, "IMAGE WARNING [%d]: " fmt, __LINE__, ## args)
#else
#define __E(fmt, args...)
#define __W(fmt, args...)
#endif

static image imagens[MAX_NUMBER_IMAGES];
static int imgId = 0;

/* Por enquanto, so cria imagens a partir de png */
int image_create(unsigned int x, unsigned int y, unsigned char *filename) {
    if (imgId > MAX_NUMBER_IMAGES) {
        __E("Nao e possivel criar mais imagens. Numero maximo.\n");
        return IMAGE_FAILURE;
    } else {
        imagens[imgId].x = x;
        imagens[imgId].y = y;
        imagens[imgId].visible = true;
        imagens[imgId].initialized = false;
        imagens[imgId].id = imgId;
        imagens[imgId].winp = NULL;
        imagens[imgId].previous = &imagens[imgId];
        imagens[imgId].next = &imagens[imgId];

        FILE *file = fopen(filename, "rb");
        if (file == NULL) {
            __E("Failed to open image file [%s]\n", filename);
            return IMAGE_FAILURE;
        }

        const char signature_png[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
        const char signature_jpeg[] = {0xFF, 0xD8, 0xFF};

        char temp[9];

        if (fread(&temp, 8, 1, file) == 8) {
            __E("Nao foi possivel criar a imagem.\n");
            return IMAGE_FAILURE;
        }

        rewind(file);

        if (!strncmp(temp, signature_png, 8)) {
            if (png_create(file, &imagens[imgId]) == JPEG_FAILURE) {
                __E("Nao foi possivel criar a imagem.\n");
                return IMAGE_FAILURE;
            } else {
                imagens[imgId].initialized = true;
                return imgId++;
            }
        } else if (!strncmp(temp, signature_jpeg, 3)) {
            if (jpeg_create(file, &imagens[imgId]) == JPEG_FAILURE) {
                __E("Nao foi possivel criar a imagem.\n");
                return IMAGE_FAILURE;
            } else {
                imagens[imgId].initialized = true;
                return imgId++;
            }
        }
    }
}

signed int image_destroy(signed int imageId) {
    if (!imagens[imageId].initialized) {
        __E("Imagem nao pode ser destruida: imagem nao foi criada.");
        return IMAGE_FAILURE;
    }

    imagens[imageId].x = 0;
    imagens[imageId].y = 0;
    imagens[imageId].w = 0;
    imagens[imageId].h = 0;
    imagens[imageId].offset_x = 0;
    imagens[imageId].offset_y = 0;
    imagens[imageId].w_visible = 0;
    imagens[imageId].h_visible = 0;
    imagens[imageId].redraw = 0;
    free(imagens[imageId].bufp);
    free(imagens[imageId].alphabufp);
    imagens[imageId].visible = 0;

    image_delFromWindow(imageId);
    imagens[imageId].initialized = 0;
    return IMAGE_SUCCESS;
}

int image_draw(int imageId) {
    if (imagens[imageId].initialized) {
        if (imagens[imageId].winp) {
            if (imagens[imageId].visible) {
                unsigned short *dst = imagens[imageId].winp->rgbBuffer + imagens[imageId].y * imagens[imageId].winp->w + imagens[imageId].x;
                unsigned short *src = imagens[imageId].bufp;
                unsigned char *alphadst = imagens[imageId].winp->attrBuffer + (imagens[imageId].winp->w * imagens[imageId].y + imagens[imageId].x) / ATTR_PIXELS_PER_BYTE;
                unsigned char *alphasrc = imagens[imageId].alphabufp;
                int i, j, k;

                unsigned char par = ((unsigned int) dst % 2) ? 0 : 1;

                for (j = 0; j < imagens[imageId].h && j < (imagens[imageId].winp->h - imagens[imageId].y); j++) {
                    for (i = 0, k = 0; i < imagens[imageId].w && i < (imagens[imageId].winp->w - imagens[imageId].x); i++) {
                        unsigned char attr_dstTemp, attr_srcTemp;
                        unsigned char dst_r, dst_g, dst_b;

                        if (par) {
                            attr_dstTemp = alphadst[k] >> 4;
                            attr_srcTemp = alphasrc[i] / 36;
                            if (attr_dstTemp != NO_TRANSPARENCY) {
                                if (alphasrc[i] == 255) {
                                    dst[i] = src[i];
                                } else if (alphasrc[i] != 0) {
                                    png_composite(dst_r, ((src[i] & 0xf800) >> 8), alphasrc[i], ((dst[i] & 0xf800) >> 8));
                                    png_composite(dst_g, ((src[i] & 0x07e0) >> 3), alphasrc[i], ((dst[i] & 0x07e0) >> 3));
                                    png_composite(dst_b, ((src[i] & 0x001f) << 3), alphasrc[i], ((dst[i] & 0x001f) << 3));

                                    dst_r = dst_r * (1 << 5) / (1 << 8);
                                    dst_g = dst_g * (1 << 6) / (1 << 8);
                                    dst_b = dst_b * (1 << 5) / (1 << 8);

                                    dst[i] = (dst_r << 11) | (dst_g << 5) | dst_b;
                                }

                                if (attr_dstTemp < attr_srcTemp) {
                                    alphadst[k] |= attr_srcTemp << 4;
                                }
                            } else {
                                dst[i] = src[i];
                                alphadst[k] |= attr_srcTemp << 4;
                            }
                            par = 0;
                        } else {
                            attr_dstTemp = alphadst[k] & 0x0f;
                            attr_srcTemp = alphasrc[i] / 36;
                            if (attr_dstTemp != NO_TRANSPARENCY) {
                                if (alphasrc[i] == 255) {
                                    dst[i] = src[i];
                                } else if (alphasrc[i] != 0) {
                                    png_composite(dst_r, ((src[i] & 0xf800) >> 8), alphasrc[i], ((dst[i] & 0xf800) >> 8));
                                    png_composite(dst_g, ((src[i] & 0x07e0) >> 3), alphasrc[i], ((dst[i] & 0x07e0) >> 3));
                                    png_composite(dst_b, ((src[i] & 0x001f) << 3), alphasrc[i], ((dst[i] & 0x001f) << 3));

                                    dst_r = dst_r * (1 << 5) / (1 << 8);
                                    dst_g = dst_g * (1 << 6) / (1 << 8);
                                    dst_b = dst_b * (1 << 5) / (1 << 8);

                                    dst[i] = (dst_r << 11) | (dst_g << 5) | dst_b;
                                }

                                if (attr_dstTemp < attr_srcTemp) {
                                    alphadst[k] |= attr_srcTemp;
                                }
                            } else {
                                dst[i] = src[i];
                                alphadst[k] |= attr_srcTemp;
                            }
                            par = 1;
                            k++;
                        }
                    }
                    alphadst += imagens[imageId].winp->w / ATTR_PIXELS_PER_BYTE;
                    alphasrc += imagens[imageId].w;
                    dst += imagens[imageId].winp->w;
                    src += imagens[imageId].w;
                }
            } else return IMAGE_SUCCESS;
        } else {
            __E("Nenhuma tela associada a imagem.\n");
            return IMAGE_FAILURE;
        }
    } else {
        __E("Imagem nao criada.\n");
        return IMAGE_FAILURE;
    }
}

int image_setVisible(int imageId, unsigned char visible) {
    if (imagens[imageId].initialized) {
        if (visible == true || visible == false) {
            imagens[imageId].visible = visible;
            return IMAGE_SUCCESS;
        } else {
            __E("Opcao invalida.\n");
            return IMAGE_FAILURE;
        }
    } else {
        __E("Imagem nao criada.\n");
        return IMAGE_FAILURE;
    }
}

int image_addToWindow(int imageId, int imageId_next, int windowId, window *winp) {
    if (imagens[imageId_next].winp == NULL) {
        imagens[imageId_next].next = imagens[imageId].next;
        imagens[imageId].next = &imagens[imageId_next];
        imagens[imageId_next].previous = &imagens[imageId];
        imagens[imageId_next].next->previous = &imagens[imageId_next];
        imagens[imageId_next].winp = winp;
        return IMAGE_SUCCESS;
    } else {
        __E("Ja existe uma tela associada a imagem.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_delFromWindow(signed int imageId) {
    if (!imagens[imageId].initialized) {
        __E("Imagem nao criada.\n");
        return IMAGE_FAILURE;
    }

    if (imagens[imageId].winp == NULL) {
        return IMAGE_SUCCESS;
    }

    if (imagens[imageId].next == &imagens[imageId]) {
        imagens[imageId].winp->firstImage = -1;
        imagens[imageId].winp->actualImage = -1;
    } else {
        if (imagens[imageId].winp->actualImage == imageId) {
            imagens[imageId].winp->actualImage = imagens[imagens[imageId].winp->actualImage].previous->id;
        } else if (imagens[imageId].winp->firstImage == imageId) {
            imagens[imageId].winp->firstImage = imagens[imagens[imageId].winp->firstImage].next->id;
        }

        imagens[imageId].previous->next = imagens[imageId].next;
        imagens[imageId].next->previous = imagens[imageId].previous;
    }
    imagens[imageId].previous = &imagens[imageId];
    imagens[imageId].next = &imagens[imageId];
    imagens[imageId].winp = NULL;
    imagens[imageId].windowId = -1;
    window_setComponentsRedraw(imagens[imageId].windowId);
    return IMAGE_SUCCESS;
}

int image_previous(int imageId) {
    if (imagens[imageId].initialized) return imagens[imageId].previous->id;
    else {
        __E("Imagem nao criada.\n");
        return IMAGE_FAILURE;
    }
}

int image_next(int imageId) {
    if (imagens[imageId].initialized) return imagens[imageId].next->id;
    else {
        __E("Imagem nao criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_getX(signed int imageId) {
    if (imagens[imageId].initialized) {
        return imagens[imageId].x;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_getY(signed int imageId) {
    if (imagens[imageId].initialized) {
        return imagens[imageId].y;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_getWidth(signed int imageId) {
    if (imagens[imageId].initialized) {
        return imagens[imageId].w;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_getHeight(signed int imageId) {
    if (imagens[imageId].initialized) {
        return imagens[imageId].h;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_isVisible(signed int imageId) {
    if (imagens[imageId].initialized) {
        return imagens[imageId].visible;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

void image_setRedraw(signed int imageId) {
    imagens[imageId].redraw = 1;
}

signed int image_setOffsetX(signed int imageId, signed int offset_x) {
    if (imageId < 0) {
        __E("Parâmetro incorreto\n");
        return IMAGE_FAILURE;
    }

    if (imagens[imageId].initialized) {
        if (offset_x > 0) {
            imagens[imageId].redraw = 1;
            if ((imagens[imageId].offset_x + imagens[imageId].w_visible + offset_x) <= imagens[imageId].w) {
                imagens[imageId].offset_x += offset_x;
            } else {
                imagens[imageId].offset_x = (imagens[imageId].w - imagens[imageId].w_visible);

                __E("Deslocamente horizontal da imagem excedeu o limite\n");
                return IMAGE_FAILURE;
            }

        } else if (offset_x < 0) {
            imagens[imageId].redraw = 1;
            if (((signed int) imagens[imageId].offset_x + offset_x) >= 0) {
                imagens[imageId].offset_x += offset_x;
            } else {
                imagens[imageId].offset_x = 0;

                __E("Deslocamente horizontal da imagem excedeu o limite\n");
                return IMAGE_FAILURE;
            }
        }
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
    return IMAGE_SUCCESS;
}

signed int image_setOffsetY(signed int imageId, signed int offset_y) {
    if (imageId < 0) {
        __E("Parâmetro incorreto\n");
        return IMAGE_FAILURE;
    }

    if (imagens[imageId].initialized) {
        if (offset_y > 0) {
            imagens[imageId].redraw = 1;
            if ((imagens[imageId].offset_y + imagens[imageId].h_visible + offset_y) <= imagens[imageId].h) {
                imagens[imageId].offset_y += offset_y;
            } else {
                imagens[imageId].offset_y = (imagens[imageId].h - imagens[imageId].h_visible);

                __E("Deslocamente vertical da imagem excedeu o limite\n");
                return IMAGE_FAILURE;
            }

        } else if (offset_y < 0) {
            imagens[imageId].redraw = 1;
            if (((signed int) imagens[imageId].offset_y + offset_y) >= 0) {
                imagens[imageId].offset_y += offset_y;
            } else {
                imagens[imageId].offset_y = 0;

                __E("Deslocamente vertical da imagem excedeu o limite\n");
                return IMAGE_FAILURE;
            }
        }
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
    return IMAGE_SUCCESS;
}

signed int image_setWVisible(signed int imageId, unsigned int w_visible) {
    if (imageId < 0) {
        __E("Parâmetro incorreto\n");
        return IMAGE_FAILURE;
    }

    if (imagens[imageId].initialized) {
        imagens[imageId].w_visible = w_visible;
        imagens[imageId].redraw = 1;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

signed int image_setHVisible(signed int imageId, unsigned int h_visible) {
    if (imageId < 0) {
        __E("Parâmetro incorreto\n");
        return IMAGE_FAILURE;
    }

    if (imagens[imageId].initialized) {
        imagens[imageId].h_visible = h_visible;
        imagens[imageId].redraw = 1;
    } else {
        __E("Imagem não foi criada.\n");
        return IMAGE_FAILURE;
    }
}

